package com.gitee.magic.jdbc.persistence.source.jdbc.mapper;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.springframework.util.CollectionUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.utils.StringUtils;
import com.gitee.magic.jdbc.persistence.source.jdbc.sqlplus.toolkit.EmptyUtils;
import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;
import com.greenpineyu.fel.context.FelContext;

/**
 * @author start
 */
public class SqlTemplate {

	public static Map<String, Node> MAPPER_NODE = new ConcurrentHashMap<>();

	/**
	 * 变量表达式
	 */
	private static final String VARIABLEREGEX="[a-zA-Z]\\w+";

	/**
	 * 参数占位符表达式#{参数}
	 */
	private static final String PARAMPLACEHOLDERREGEX="#\\{[\\w\\.]*\\}";
	
	private static final String MAPPER_STR="mapper";
	private static final String SQL_TAG="sql";
	private static final String ATT_ID="id";
	
	private static final String INCLUDE_TAG="include";
	private static final String ATT_REFID="refid";

	private static final String IF_WHERE="where";
	private static final String IF_TAG="if";
	private static final String IF_CONDITION_TAG="condition";
	
	private static final String FOR_TAG="for";
	private static final String COLLECTION_TAG="collection";
	private static final String ITEM_TAG="item";
	
	private static final String FOREACH_TAG="foreach";
	private static final String FOREACH_OPEN_TAG="open";
	private static final String FOREACH_CLOSE_TAG="close";
	private static final String FOREACH_SEPARATOR_TAG="separator";

	public static void analysisXml(InputStream inputStream) {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		Document document=null;
		try {
			DocumentBuilder db=dbf.newDocumentBuilder();
			document = db.parse(inputStream);
		} catch (SAXException e) {
			throw new ApplicationException(e);
		} catch (IOException e) {
			throw new ApplicationException(e);
		} catch (ParserConfigurationException e) {
			throw new ApplicationException(e);
		}
        NodeList sqls = document.getChildNodes();
        for (int i = 0; i < sqls.getLength(); i++) {
        	Node node = sqls.item(i);
        	if(MAPPER_STR.equals(node.getNodeName())) {
        		NodeList nodeLists = node.getChildNodes();
        		for (int j = 0; j < nodeLists.getLength(); j++) {
        			Node nodeItem = nodeLists.item(j);
        			String nodeName=nodeItem.getNodeName();
        			if(SQL_TAG.equals(nodeName)) {
        				String id=getNodeAttribute(nodeItem, ATT_ID);
            			if(SqlTemplate.MAPPER_NODE.containsKey(id)) {
            				throw new ApplicationException("映射语句ID："+id+",已存在，请重新定义标签名");
            			}
            			SqlTemplate.MAPPER_NODE.put(id, nodeItem);
        			}
        		}
        	}else {
        		//跳过不是mapper的映射文件
        		return;
        	}
        }
	}
	
	/**
	 * 获取当前Node的所有内容
	 * @param node
	 * @param parameter
	 * @return
	 */
	public static String getNodeChildTextContent(Node node,Map<String,Object> parameter) {
		StringBuilder sqlStr=new StringBuilder();
		NodeList nodeList=node.getChildNodes();
		for(int i=0;i<nodeList.getLength();i++) {
			Node cNode=nodeList.item(i);
			sqlStr.append(analysisNode(cNode,parameter));
		}
		//${参数}替换处理
		for(String s:parameter.keySet()) {
			Object val=parameter.get(s);
			if(val!=null) {
				String p="${"+s+"}";
				while(true) {
					int i=sqlStr.indexOf(p);
					if(i>=0) {
						sqlStr=sqlStr.replace(i,i+p.length(),String.valueOf(val));
					}else {
						break;
					}
				}
			}
		}
		return sqlStr.toString();
	}
	
	/**
	 * 标签属性
	 * @param node
	 * @param attributeName
	 * @return
	 */
	public static String getNodeAttribute(Node node,String attributeName) {
		return getNodeAttribute(node,attributeName,true);
	}
	
	/**
	 * 标签属性
	 * @param node
	 * @param attributeName
	 * @return
	 */
	public static String getNodeAttribute(Node node,String attributeName,boolean flag) {
		NamedNodeMap attMaps=node.getAttributes();
		String attributeValue=null;
		for(int i=0;i<attMaps.getLength();i++) {
			Node attNode=attMaps.item(i);
			if(attributeName.equals(attNode.getNodeName())) {
				attributeValue=attNode.getNodeValue();
			}
		}
		if(flag) {
			if(StringUtils.isEmpty(attributeValue)) {
				throw new ApplicationException(node.getNodeName()+"标签，"+attributeName+"属性不能为空");
			}
		}
		return attributeValue;
	}
	
	public static String analysisNode(Node node,Map<String,Object> parameter) {
		int nta=1;
		int ntc=3;
		int ntd=4;
		int nth=8;
		if(node.getNodeType()==nta) {
			//标签
			String nodeName=node.getNodeName();
			if(SQL_TAG.equals(nodeName)) {
				return getNodeChildTextContent(node,parameter);
			}else if(IF_WHERE.equals(nodeName)) {
				String whereSql=getNodeChildTextContent(node,parameter);
				whereSql=formatSql(whereSql);
				if(StringUtils.isEmpty(whereSql)) {
					return "";
				}
				String[] startWith= {"AND ","OR "};
				for(String w:startWith) {
					if(whereSql.toUpperCase().startsWith(w)) {
						return "WHERE "+whereSql.substring(w.length());
					}
				}
				return "WHERE "+whereSql;
			}else if(IF_TAG.equals(nodeName)) {
//				ScriptEngineManager manager = new ScriptEngineManager();  
//				ScriptEngine engine = manager.getEngineByName("js");
				FelEngine felEngine = new FelEngineImpl();
				FelContext engine = felEngine.getContext();
    			String condition=getNodeAttribute(node,IF_CONDITION_TAG);
    			//and or 逻辑条件转换
    			condition=condition.replaceAll(" or ", " || ").replaceAll(" and ", " && ");
    			Pattern pattern = Pattern.compile(VARIABLEREGEX);
    			Matcher matcher = pattern.matcher(condition);
    			while(matcher.find()){
    				String e = matcher.group();
//    				engine.put(e, parameter.get(e));
    				engine.set(e, parameter.get(e));
    			}
//    			Object result =null;
//    			try {
////					result = engine.eval(condition);
//				} catch (ScriptException e) {
//					throw new ApplicationException(e);
//				}
    			Object result = felEngine.eval(condition, engine);
    			if(result!=null) {
    				if(result.getClass().equals(Boolean.class)) {
        				if(((Boolean)result)) {
        					return getNodeChildTextContent(node,parameter);
        				}
        			}else {
        				throw new ApplicationException("if标签表达式返回类型必须为Boolean");
        			}
    			}
    			return "";
			}else if(FOR_TAG.equals(nodeName)) {
				//TODO:即将去除foreach代替
    			List<String> dataList=getForEachDataList(node,parameter);
				StringBuilder newSqlStr=new StringBuilder();
				for(String sql:dataList) {
					newSqlStr.append(sql);
				}
				return newSqlStr.toString();
			}else if(FOREACH_TAG.equals(nodeName)) {
				List<String> newSqlArr=getForEachDataList(node,parameter);
				if(CollectionUtils.isEmpty(newSqlArr)) {
					return "";
				}
				String open=getNodeAttribute(node, FOREACH_OPEN_TAG);
				String close=getNodeAttribute(node, FOREACH_CLOSE_TAG,false);
				String separtor=getNodeAttribute(node, FOREACH_SEPARATOR_TAG);
				StringBuilder newSqlStr=new StringBuilder();
				newSqlStr.append(open);
				newSqlStr.append(StringUtils.listToString(newSqlArr, separtor));
				newSqlStr.append(close);
				return newSqlStr.toString();
			}else if(INCLUDE_TAG.equals(nodeName)) {
    			String refid=getNodeAttribute(node,ATT_REFID);
    			return analysisNode(MAPPER_NODE.get(refid),parameter);
			}else {
				throw new ApplicationException("无法解析"+nodeName+"标签");
			}
		}else if(node.getNodeType()==ntc) {
			//文本#Text
			return node.getTextContent();
		}else if(node.getNodeType()==ntd) {
			//文本<![CDATA[ 转义字符处理 ]]>
			return node.getTextContent();
		}else if(node.getNodeType()==nth) {
			//注释<!---->
			return "";
		}else {
			//其它
			throw new ApplicationException("无法解析NodeType:"+node.getNodeType()+"标签类型");
		}
	}

	/**
	 * 获取ForEach数据列表
	 * @param node
	 * @param parameter
	 * @return
	 */
	public static List<String> getForEachDataList(Node node,Map<String,Object> parameter){
		List<String> newSqlArray=new ArrayList<>();
		String collection=getNodeAttribute(node, COLLECTION_TAG);
		Object array=parameter.get(collection);
		if(array==null) {
			return newSqlArray;
		}
		if(!List.class.isAssignableFrom(array.getClass())) {
			throw new ApplicationException(node.getNodeName()+"标签，collection值必须为List的子类");
		}
//		String item=getNodeAttribute(node, ITEM_TAG);
		String item=getNodeAttribute(node, ITEM_TAG,false);
		String resultSql=getNodeChildTextContent(node,parameter);
		Pattern pattern = Pattern.compile(PARAMPLACEHOLDERREGEX);
		if(item==null) {
			@SuppressWarnings("unchecked")
			List<Object> datas=(List<Object>)array;
			for(int j=0;j<datas.size();j++) {
				Object val=datas.get(j);
				int i=0;
				Matcher matcher = pattern.matcher(resultSql);
				String foreachSql=resultSql;
				while(matcher.find()){
					i++;
					String e = matcher.group();
					String key=e.substring(2, e.length()-1);
					String random=StringUtils.random();
					String newKey=random+"_"+key+"_"+j+"_"+i;
					foreachSql=foreachSql.replace(e, "#{"+newKey+"}");
					parameter.put(newKey, ParseBean.parse(val,key));
				}
				newSqlArray.add(foreachSql);
			}
		}else {
			Matcher matcher = pattern.matcher(resultSql);
			while(matcher.find()){
				String e = matcher.group();
				String key=e.substring(2, e.length()-1);
				if(key.equals(item)) {
					String random=StringUtils.random();
					@SuppressWarnings("unchecked")
					List<Object> datas=(List<Object>)array;
					for(int j=0;j<datas.size();j++) {
						String newKey=random+"_"+item+"_"+j;
						newSqlArray.add(resultSql.replace(e, "#{"+newKey+"}"));
						parameter.put(newKey, datas.get(j));
					}
				}else {
					
				}
			}
		}
		return newSqlArray;
	}
	
	/**
	 * 根据id获取对应的SQL模板
	 * @param id
	 * @return
	 */
	public synchronized static String getMapperSqlTemplate(String id,Map<String,Object> parameter) {
		if(MAPPER_NODE.containsKey(id)) {
			String sql=analysisNode(MAPPER_NODE.get(id), parameter);
			return formatSql(sql);
		}else {
			throw new ApplicationException("不存在ID:"+id+"的语句映射");
		}
	}
	
	/**
	 * 获取可执行的JDBC语句
	 * @param sql
	 * @return
	 */
	public static String getExecuteSql(String sql) {
		return sql.replaceAll(PARAMPLACEHOLDERREGEX, "?");
	}
	
	/**
	 * 获取可执行的JDBC参数
	 * @param sql
	 * @param parameter
	 * @return
	 */
	public static Object[] getExecuteParameter(String sql,Map<String,Object> parameter) {
		List<Object> params=new ArrayList<>();
		if(!EmptyUtils.isEmpty(parameter)) {
			Pattern pattern = Pattern.compile(PARAMPLACEHOLDERREGEX);
			Matcher matcher = pattern.matcher(sql);
			while(matcher.find()){
				String e = matcher.group();
				String key=e.substring(2, e.length()-1);
				params.add(parameter.get(key));
			}
		}
		return params.toArray();
	}
	
	/**
	 * 格式化
	 * @param sql
	 * @return
	 */
	public static String formatSql(String sql) {
		//Sql语句压缩
		sql=sql.replaceAll("\n", "").replaceAll("\t", " ").trim();
		String space="  ";
		while(sql.indexOf(space)>0) {
			sql=sql.replaceAll(space, " ");
		}
		return sql;
	}
	
}
